package com.siashan.toolkit.api.security.advice;

import com.siashan.toolkit.api.security.RestCryptoHttpInputMessage;
import com.siashan.toolkit.api.security.anno.ApiSignEnable;
import com.siashan.toolkit.api.security.dto.RequestDto;
import com.siashan.toolkit.api.security.exception.RestCryptoException;
import com.siashan.toolkit.api.security.manager.RestCryptoManager;
import com.siashan.toolkit.api.security.plugin.NonceStrVerifyPlugin;
import com.siashan.toolkit.api.security.plugin.TimeStampVerifyPlugin;
import com.siashan.toolkit.api.security.plugin.AppIdVerifyPlugin;
import com.siashan.toolkit.api.security.properties.RestCryptoProperties;
import com.siashan.toolkit.api.security.properties.SignProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * 接口请求数据解密
 *
 * @author siashan
 * @version V1.0.1
 **/
@ControllerAdvice
@Slf4j
public class RestCryptoRequestBodyAdvice implements RequestBodyAdvice {

    /**
     * 是否验签
     */
    private boolean encrypt;

    @Autowired
    private RestCryptoProperties restCryptoProperties;
    @Autowired(required = false)
    private RestCryptoManager restCryptoManager;


    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 只有配置文件加密验签开启和接口上有加密验签注解同时存在才走该类
        SignProperties sign = restCryptoProperties.getSign();
        Method method = methodParameter.getMethod();
        if (method.isAnnotationPresent(ApiSignEnable.class) && (null != sign && sign.isInSign())) {
            encrypt = true;
        }
        return encrypt;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
                                           Class<? extends HttpMessageConverter<?>> converterType) {

        if (encrypt) {
            try {
                //获取注解配置的包含和去除字段
                ApiSignEnable cryptoEnable = parameter.getMethodAnnotation(ApiSignEnable.class);
                if (cryptoEnable.inSign()) {
                    // 如果接口验签配置为true
                    RestCryptoHttpInputMessage restCryptoHttpInputMessage = new RestCryptoHttpInputMessage(inputMessage, restCryptoProperties);
                    // 接口安全行校验
                    apiSecurityCheck(restCryptoHttpInputMessage.getRequestDto());
                    return restCryptoHttpInputMessage;
                } else {
                    // 如果接口注解验签配置为false
                    return inputMessage;
                }
            } catch (Exception e) {
                log.warn("对方法method :【" + parameter.getMethod().getName() + "】数据进行解密出现异常" , e);
                throw new RestCryptoException("对方法method :【" + parameter.getMethod().getName() + "】数据进行解密出现异常" , e);
            }
        }
        return inputMessage;
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
                                Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }


    /**
     * 接口安全校验
     *
     * @param requestDto 请求参数
     */
    private void apiSecurityCheck(RequestDto requestDto) {

        if (null != restCryptoManager) {
            // AppId合法性校验
            AppIdVerifyPlugin restAppIdPlugin = restCryptoManager.getAppIdVerifyPlugin();
            if (null != restAppIdPlugin) {
                restAppIdPlugin.verify(requestDto.getAppId());
            }
            // 时间戳合法性校验
            TimeStampVerifyPlugin timeStampVerifyPlugin = restCryptoManager.getTimeStampVerifyPlugin();
            if (null != timeStampVerifyPlugin) {
                timeStampVerifyPlugin.verify(requestDto.getTimeStamp());
            }
            // 随机数合法性校验
            NonceStrVerifyPlugin nonceStrVerifyPlugin = restCryptoManager.getNonceStrVerifyPlugin();
            if (null != nonceStrVerifyPlugin) {
                nonceStrVerifyPlugin.verify(requestDto.getNonceStr());
            }
        }
    }

}
